using System;
using System.Collections;
using System.Collections.Generic;

using FineCollection.Intf;




namespace FineCollection.Cont
{
    /// <summary>
    /// Array-based matrix.
    /// </summary>
    /// <typeparam name="T">type of item.</typeparam>
    public class ArrMatrix<T> : WMatrix<T>
    {
        /// <summary>
        /// Zero-based array.
        /// </summary>
        private T[] arr;


        /// <summary>
        /// Trivial constructor.
        /// </summary>
        /// <param name="firstY">first row index.</param>
        /// <param name="lastY">last row index.</param>
        /// <param name="firstX">first column index.</param>
        /// <param name="lastX">last column index.</param>
        public ArrMatrix(int firstY, int lastY, int firstX, int lastX)
        {
            if (firstY > lastY)
                throw new ArgumentException("Y dimansion is invalid");
            if (firstX > lastX)
                throw new ArgumentException("X dimansion is invalid");

            this.firstX = firstX;
            this.lastX = lastX;
            this.firstY = firstY;
            this.lastY = lastY;

            int cnt = SizeX * SizeY;

            arr = new T[cnt];
        }



        #region COORDINATES AREA

        /// <summary>
        /// The first X-index.
        /// </summary>
        public int FirstX
        {
            get { return firstX; }
        }

        private int firstX;


        /// <summary>
        /// The last X-index.
        /// </summary>
        public int LastX
        {
            get { return lastX; }
        }

        private int lastX;


        /// <summary>
        /// The first Y-index.
        /// </summary>
        public int FirstY
        {
            get { return firstY; }
        }

        private int firstY;


        /// <summary>
        /// The last Y-index.
        /// </summary>
        public int LastY
        {
            get { return lastY; }
        }

        private int lastY;


        /// <summary>
        /// Count of columns.
        /// Equals to LastX-FirstX+1.
        /// </summary>
        public int SizeX
        {
            get { return lastX - firstX + 1; }
        }


        /// <summary>
        /// Count of rows.
        /// Equals to LastY-FirstY+1.
        /// </summary>
        public int SizeY
        {
            get { return lastY - firstY + 1; }
        }

        #endregion


        private int FlatIndex(int y, int x)
        {
            if (y < firstY || y > lastY)
                throw new IndexOutOfBoundsException(y, firstY, lastY);

            if (x < firstX || x > lastX)
                throw new IndexOutOfBoundsException(x, firstX, lastX);

            return (y - firstY)*SizeX + (x - firstX);
        }


        /// <summary>
        /// Item accessor (writeable).
        /// </summary>
        /// <param name="y">index of the row.</param>
        /// <param name="x">index of the column.</param>
        /// <returns>desired item.</returns>
        public T this[int y, int x]
        {
            get
            {
                int fi = FlatIndex(y, x);
                return arr[fi];
            }

            set
            {
                int fi = FlatIndex(y, x);
                arr[fi] = value;
            }
        }


        public RRandomList<T> Row(int y)
        {
            return new RowView<T>(this, y);
        }



        #region ROW-VIEW

        private class RowView<TT> : RRandomList<TT>
        {
            private ArrMatrix<TT> matrix;
            private int offset;


            public RowView(ArrMatrix<TT> matrix, int rowIndex)
            {
                this.matrix = matrix;
                offset = (rowIndex - matrix.firstY)*matrix.SizeX - matrix.firstX;
            }


            public int FirstIndex
            {
                get { return matrix.firstX; }
            }

            public int LastIndex
            {
                get { return matrix.lastX; }
            }


            public TT this[int x]
            {
                get
                {
                    if (x < matrix.firstX || x > matrix.lastX)
                        throw new IndexOutOfBoundsException(x, matrix.firstX, matrix.lastX);

                    return matrix.arr[offset + x];
                }
            }


            TT RCollection<TT>.this[Predicate<TT> predicate]
            {
                get { return Find(predicate, default(TT)); }
            }


            public TT Find(Predicate<TT> predicate, TT defaultValue)
            {
                for (int j = offset; j < offset + matrix.SizeX; j++)
                    if (predicate(matrix.arr[j]))
                        return matrix.arr[j];
                return defaultValue;
            }


            public int IndexOf(Predicate<TT> predicate)
            {
                return IndexOf(predicate, matrix.firstX);
            }


            public int IndexOf(Predicate<TT> predicate, int startFromIndex)
            {
                if (startFromIndex < matrix.firstX || startFromIndex > matrix.lastX)
                    throw new IndexOutOfBoundsException(startFromIndex, matrix.firstX, matrix.lastX);
                for (int k = startFromIndex; k <= matrix.lastY; k++)
                    if (predicate(this[k]))
                        return k;
                return int.MinValue;
            }


            public TT First
            {
                get { return matrix.arr[offset]; }
            }

            public TT Last
            {
                get { return matrix.arr[offset + matrix.SizeX - 1]; }
            }

            public int Count
            {
                get { return matrix.SizeX; }
            }

            public bool IsEmpty
            {
                get { return false; }
            }

            public bool IsNotEmpty
            {
                get { return true; }
            }


            public RIterator<TT> TakeIterator()
            {
                return new ArrIterator<TT>(matrix.arr, offset, matrix.SizeX);
            }


            public IEnumerator<TT> GetEnumerator()
            {
                return new ArrIterator<TT>(matrix.arr, offset, matrix.SizeX);
            }


            IEnumerator IEnumerable.GetEnumerator()
            {
                return new ArrIterator<TT>(matrix.arr, offset, matrix.SizeX);
            }
        }

        #endregion
    }
}